/**
 * Copyright (c) 2018, Alex. All rights reserved.
 */
package com.oschina.bottle.convert.loader.scanner;

import com.oschina.bottle.convert.Converter;
import com.oschina.bottle.convert.impl.DefaultBeanConverter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
 * 扫描
 *
 * @author <a href="mailto:zhongchao@gegejia.com">zhong</a>
 * @version 1.0 2018/1/20
 * @since 1.0
 */
public class ConverterClassPathScanner {

    /**
     * 扫描范围为所有的class对象
     */
    private final static String RESOURCE_PATTERN = "/**/*.class";

    private String[] packagesToScan;

    private final ResourcePatternResolver resourcePatternResolver;

    private static final TypeFilter CID_PROCESSOR_TYPE_FILTER = new AssignableTypeFilter(DefaultBeanConverter.class) {
        /**
         * @see org.springframework.core.type.filter.AssignableTypeFilter#matchClassName(java.lang.String)
         */
        @Override
        protected boolean matchClassName(String className) {
            return false;
        }
    };

    public ConverterClassPathScanner(ClassLoader classLoader, String... packagesToScan) {
        Assert.notEmpty(packagesToScan, "'packagesToScan' must not be empty");
        this.packagesToScan = packagesToScan;
        this.resourcePatternResolver = new PathMatchingResourcePatternResolver(classLoader);
    }

    public List<Converter> doScan() {
        List<Converter> converters = new ArrayList<>();

        for (String packageToScan : packagesToScan) {
            String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils
                .convertClassNameToResourcePath(packageToScan) + RESOURCE_PATTERN;
            try {
                Resource[] resources = this.resourcePatternResolver.getResources(pattern);
                MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(
                    this.resourcePatternResolver);
                for (Resource resource : resources) {

                    MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
                    if (isConverter(metadataReader, metadataReaderFactory)) {
                        String className = metadataReader.getClassMetadata().getClassName();
                        Class<?> modelConverterClass = this.resourcePatternResolver.getClassLoader()
                            .loadClass(className);
                        converters.add((Converter) modelConverterClass.newInstance());
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException("Failed to scan Processor classes", e);
            }
        }

        return converters;
    }

    protected boolean isConverter(MetadataReader reader, MetadataReaderFactory factory) throws IOException {
        return CID_PROCESSOR_TYPE_FILTER.match(reader, factory);
    }
}
